package com.fare.taluo.core.domain;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.fare.taluo.conf.PropertyUtils;
import com.fare.taluo.core.domain.config.channel.SourceEntity;
import com.fare.taluo.core.domain.crawl.CrawlPropertiesLoader;
import com.fare.taluo.core.domain.crawl.ProxyIpHttpFailException;
import com.fare.taluo.em.TimePeriod;
import com.foreveross.crawl.adapter.AbstractAdapter;
import com.foreveross.crawl.adapter.CrawlAdapterFactory;
import com.foreveross.crawl.common.GlobalsConst;
import com.foreveross.crawl.common.cfg.ConfigContainer;
import com.foreveross.crawl.common.util.SpringHelper;
import com.foreveross.crawl.domain.freight.PlaneInfoEntity;
import com.foreveross.crawl.domain.task.TaskQueueRecordEntity;
import com.foreveross.crawl.exception.FlightInfoNotFoundException;
import com.foreveross.crawl.exception.NoTicketException;
import com.foreveross.proxyip.IProxyIpProvider;
import com.foreveross.proxyip.ProxyIpModel;
import com.foreveross.proxyip.ProxyIpProviderFactory;
import com.foreveross.proxyip.ProxyipProperties;

public class FlightPrice {

	static {
		ConfigContainer.getInstance().register(ProxyipProperties.class);
		ApplicationContext ctx = new ClassPathXmlApplicationContext(
				"spring/applicationContext.xml");
		SpringHelper.setApplicationContext(ctx);
	}
	public Logger logger = LoggerFactory.getLogger(FlightPrice.class);
	/**
	 * 起始城市区域编码
	 */
	private String areaCode;
	/**
	 * 起始城市区域名称
	 */
	private String areaName;

	private String fromCity;

	private String fromCityName;

	private String toCity;

	private String toCityName;

	private String flag;// 判断是否用选择了使用了IP代理抓取
	/**
	 * 航班日期，格式：yyyy-MM-dd
	 */
	private String flightDate;
	/**
	 * 航班出发时段，格式为：
	 */
	private String[] flightTimes;

	// private EntityRepository repository;

	// private FilterTimePeriod periodFilter;

	private String carrierKey;// 航空公司的key

	public FlightPrice() {
		// repository = InstanceFactory.getInstance(EntityRepository.class);
	}

	/**
	 * 抓取【某个】公司【某天】【某条航线】的【最低价】航班
	 * 
	 * @return
	 */
	public PlaneInfoEntity crawlLowestPlaneInfoByCompany() throws Exception {
		FilterTimePeriod filter = new FilterTimePeriod();
		PlaneInfoEntity finalResult = null;
		List<PlaneInfoEntity> fs = crawlThreadStart();
		List<PlaneInfoEntity> ffs = new ArrayList<PlaneInfoEntity>();

		if (fs != null && !fs.isEmpty()) {
			for (PlaneInfoEntity p : fs) {
				if (filter.filter(p) == true) {
					ffs.add(p);
				}
			}
		}
		finalResult = comparableLowerPrice(ffs);
		return finalResult;
	}

	/**
	 * 抓取【某个】公司【某天】【某条航线】的【所有】航班
	 * 
	 * @return
	 * @throws Exception
	 */
	public List<PlaneInfoEntity> crawlAllPlaneInfosByCompany() throws Exception {
		return crawlThreadStart();
	}

	/**
	 * 真正执行抓取的线程开始 抛出异常代表整个抓取过程除了错 如果木有异常但是返回结果空或者empty则是木有该航线数据
	 * 
	 * @return
	 * @throws Exception
	 */
	private List<PlaneInfoEntity> crawlThreadStart() throws Exception {
		List<PlaneInfoEntity> finalResult = null;
		ExecutorService es = null;
		Future<List<PlaneInfoEntity>> future = null;
		SourceEntity sourceEntity = null;
		TaskQueueRecordEntity taskQueue = null;
		try {
			es = Executors.newFixedThreadPool(1);
			sourceEntity = SourceEntity.loadCompanyByCarrierKey(carrierKey);
			taskQueue = new TaskQueueRecordEntity(areaCode, areaName, fromCity,
					fromCityName, toCity, toCityName, flightDate,
					sourceEntity.getName(), sourceEntity.getId(),
					sourceEntity.getHbaseKey());
			future = es.submit(new CrawlTask(taskQueue));
			finalResult = future.get();
		} finally {
			es = null;
			future = null;
			sourceEntity = null;
			taskQueue = null;
		}
		return finalResult;
	}

	/**
	 * 比较航线中的最低价
	 * 
	 * @param tempList
	 * @return　集合中的最低价对象
	 */
	private PlaneInfoEntity comparableLowerPrice(List<PlaneInfoEntity> tempList) {
		PlaneInfoEntity temp = null;
		if (tempList != null) {
			for (PlaneInfoEntity e : tempList) {
				if (temp == null) {
					temp = e;
				} else if (e.getLowerPrice().doubleValue() < temp
						.getLowerPrice().doubleValue()) {
					temp = e;
				}
			}
		}
		return temp;
	}

	public String getAreaCode() {
		return areaCode;
	}

	public void setAreaCode(String areaCode) {
		this.areaCode = areaCode;
	}

	public String getCarrierKey() {
		return carrierKey;
	}

	public void setCarrierKey(String carrierKey) {
		this.carrierKey = carrierKey;
	}

	public String getAreaName() {
		return areaName;
	}

	public void setAreaName(String areaName) {
		this.areaName = areaName;
	}

	public String getFromCity() {
		return fromCity;
	}

	public void setFromCity(String fromCity) {
		this.fromCity = fromCity;
	}

	public String getFromCityName() {
		return fromCityName;
	}

	public void setFromCityName(String fromCityName) {
		this.fromCityName = fromCityName;
	}

	public String getToCity() {
		return toCity;
	}

	public String getFlag() {
		return flag;
	}

	public void setFlag(String flag) {
		this.flag = flag;
	}

	public void setToCity(String toCity) {
		this.toCity = toCity;
	}

	public String getToCityName() {
		return toCityName;
	}

	public void setToCityName(String toCityName) {
		this.toCityName = toCityName;
	}

	public String getFlightDate() {
		return flightDate;
	}

	public void setFlightDate(String flightDate) {
		this.flightDate = flightDate;
	}

	public String[] getFlightTimes() {
		return flightTimes;
	}

	public void setFlightTimes(String[] flightTimes) {
		this.flightTimes = flightTimes;
	}

	/**
	 * 抓取任务线程
	 * 
	 * 
	 */
	class CrawlTask implements Callable<List<PlaneInfoEntity>> {
		private TaskQueueRecordEntity taskQueue;
		private IProxyIpProvider proxyIpProvider;
		// private realtimeCrawlAction flag;
		// FlightPrice flag=null;
		// FlightPrice flag=new FlightPrice();
		String flags = flag;

		// System.out.println("");
		public CrawlTask(TaskQueueRecordEntity taskQueue) {
			this.taskQueue = taskQueue;
			proxyIpProvider = ProxyIpProviderFactory.getDefaultProxyProvider();
		}

		private ProxyIpModel getProxyIpModel(AbstractAdapter adapter,
				TaskQueueRecordEntity task) {
			ProxyIpModel proxyIp = null;
			Long stime = System.currentTimeMillis();
			if (ProxyipProperties.isProxyEnable() && proxyIpProvider != null
					&& flags != null && flags != "0") {
				// 3.1获得代理IP
				proxyIp = proxyIpProvider.provideProxyIp(task
						.getGrabChannelId());
				if (proxyIp != null) { // 若是代理不为空，则需要记录代理ip的使用渠道，以使代理ip服务回收
					proxyIp.setUserId(task.getGrabChannelId());
					if (StringUtils.isNotBlank(task.getProxyIp())) {
						task.setProxyIp(task.getProxyIp()
								+ GlobalsConst.SEPARATOR_LINE
								+ (proxyIp.getIp() + ":" + proxyIp.getPort()));
					} else {
						task.setProxyIp(proxyIp.getIp() + ":"
								+ proxyIp.getPort());
					}
				} else {
					if (StringUtils.isBlank(task.getProxyIp())) {
						task.setProxyIp("没有获得IP");
					} else {
						task.setProxyIp(task.getProxyIp()
								+ GlobalsConst.SEPARATOR_LINE + "没有获得IP");
					}
				}
				// 记录获得代理ip耗时
				task.setProxyIpDurationTime(System.currentTimeMillis() - stime);
				if (logger.isInfoEnabled()) {
					logger.info("获取代理ip,耗时：" + task.getProxyIpDurationTime());
				}
			}
			adapter.setProxyIp(proxyIp);
			return proxyIp;
		}

		// 同步crawl项目的适配器，返回PlaneInfoEntity实体
		@Override
		public List<PlaneInfoEntity> call() throws Exception {
			List<PlaneInfoEntity> finalResult = new ArrayList<PlaneInfoEntity>();
			AbstractAdapter adapter = null;
			List<Object> result = null;
			Object obj = null;
			// FlightPrice flag=null;
			ProxyIpModel proxyIpModel = null;
			int fetchCount = PropertyUtils.getIntProperty(
					"crawl.fail.repeat.times",
					CrawlPropertiesLoader.CONFIG_FILE);
			try {
				while (fetchCount > 0) {
					adapter = CrawlAdapterFactory.buildAdaptor(taskQueue);
					fetchCount--;
					try {
						logger.info(String.format(
								"倒数第%s次开始执行抓取%s,航线%s-%s,航班日期%s", fetchCount,
								taskQueue.getGrabChannel(),
								taskQueue.getFromCityName(),
								taskQueue.getToCityName(),
								taskQueue.getFlightDate()));
						proxyIpModel = this.getProxyIpModel(adapter, taskQueue);
						obj = adapter.fetch(null);
					} catch (Exception e) {
						// 如果抛出的异常是拒绝连接，或ip路由错误，一般是代理ip错误，需要重新抓取
						if (fetchCount > 0
								&& ProxyIpHttpFailException
										.isProxyIpException(e)) {
							continue;
						} else {
							throw e;
						}
					}
					try {
						logger.info(String.format("开始验证%s数据！",
								taskQueue.getGrabChannel()));
						if (adapter.validateFetch(obj)) { // 抓取的数据是错误的，比如服务器返回的是404或500错误，更或者是"系统正繁忙"等
							logger.info(String.format("开始解析%s数据！",
									taskQueue.getGrabChannel()));
							result = adapter.paraseToVo(obj);
							logger.info(String.format("解析%s数据后航班数%s！",
									taskQueue.getGrabChannel(),
									result == null ? 0 : result.size()));
							break;
						}
					} catch (Exception e) {
						if (fetchCount <= 0) {
							if (e instanceof FlightInfoNotFoundException
									|| e instanceof NoTicketException) {
								logger.info(String.format("渠道[%s]没有航班,抓取成功",
										taskQueue.getGrabChannel()));
							} else {
								throw e;
							}
						}
					}
				}
				// 将抓取结果存入hbase数据库
				if (result != null && !result.isEmpty()) {
					for (Object o : result) {
						PlaneInfoEntity p = (PlaneInfoEntity) o;
						// 暂时不要存储，日后再好好商榷
						finalResult.add(p);
					}
				}
			} finally {
				obj = null;
				adapter = null;
				result = null;
				if (proxyIpProvider != null) {
					try {
						// 回收代理ip
						proxyIpProvider.rollBackProxyIp(proxyIpModel);
					} catch (Exception e) {
						logger.error(e.getMessage());
					}
				}
			}
			return finalResult;
		}

	}

	/**
	 * 过滤时间段，判断航班起飞时间是否在指定时间区间内
	 * 
	 * @author xiangsf
	 * 
	 */
	class FilterTimePeriod {
		private List<TimePeriod> timePeriods;

		public FilterTimePeriod() {
			timePeriods = new ArrayList<TimePeriod>();
			if (flightTimes == null || flightTimes.length >= 4)
				timePeriods.add(TimePeriod.ALL);
			else {
				for (String s : flightTimes) {
					timePeriods.add(TimePeriod.fromCode(s.trim()));
				}
			}
		}

		public boolean filter(PlaneInfoEntity priceEntity) {
			if (priceEntity == null)
				return false;
			for (TimePeriod p : timePeriods) {
				if (p.hasBetweenTime(priceEntity.getStartTime()))
					return true;
			}
			return false;
		}
	}

}
